package net.fruit.mq.client.impl.rocketmq;

import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.message.MessageQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.fruit.mq.client.Message;
import net.fruit.mq.client.PropertyKeyConst;
import net.fruit.mq.client.SendResult;
import net.fruit.mq.client.exception.LMQClientException;
import net.fruit.mq.client.impl.tracehook.MQClientSendMessageHookImpl;
import net.fruit.mq.client.impl.util.ClientLoggerUtil;
import net.fruit.mq.client.order.OrderProducer;
import net.fruit.mq.client.trace.common.MQTraceConstants;
import net.fruit.mq.client.trace.dispatch.AsyncDispatcher;
import net.fruit.mq.client.trace.dispatch.impl.AsyncArrayDispatcher;

public class OrderProducerImpl extends MQClientAbstract implements OrderProducer {
    private final static Logger log = ClientLoggerUtil.getClientLogger();
    private final DefaultMQProducer defaultMQProducer;
    private final AtomicBoolean started = new AtomicBoolean(false);
    private AsyncDispatcher traceDispatcher = null;

    public OrderProducerImpl(final Properties properties) {
        super(properties);
        this.defaultMQProducer = new DefaultMQProducer(new ClientRPCHook(sessionCredentials));

        String producerGroup = properties.getProperty(PropertyKeyConst.ProducerId, "__MQ_PRODUCER_DEFAULT_GROUP");
        this.defaultMQProducer.setProducerGroup(producerGroup);

        boolean isVipChannelEnabled = Boolean.parseBoolean(properties.getProperty(PropertyKeyConst.isVipChannelEnabled, "false"));
        this.defaultMQProducer.setVipChannelEnabled(isVipChannelEnabled);

        String sendMsgTimeoutMillis = properties.getProperty(PropertyKeyConst.SendMsgTimeoutMillis, "3000");
        this.defaultMQProducer.setSendMsgTimeout(Integer.parseInt(sendMsgTimeoutMillis));

        this.defaultMQProducer.setInstanceName(this.buildIntanceName());
        this.defaultMQProducer.setNamesrvAddr(this.getNameServerAddr());
        // 为Producer增加消息轨迹回发模块
        try {
            Properties tempProperties = new Properties();
            tempProperties.put(MQTraceConstants.AccessKey, sessionCredentials.getAccessKey());
            tempProperties.put(MQTraceConstants.SecretKey, sessionCredentials.getSecretKey());
            tempProperties.put(MQTraceConstants.MaxMsgSize, "128000");
            tempProperties.put(MQTraceConstants.AsyncBufferSize, "2048");
            tempProperties.put(MQTraceConstants.MaxBatchNum, "10");
            tempProperties.put(MQTraceConstants.NAMESRV_ADDR, this.getNameServerAddr());
            tempProperties.put(MQTraceConstants.InstanceName, this.buildIntanceName());
            traceDispatcher = new AsyncArrayDispatcher(tempProperties);
            traceDispatcher.start(null, this.defaultMQProducer.getInstanceName());
            this.defaultMQProducer.getDefaultMQProducerImpl().registerSendMessageHook(
                    new MQClientSendMessageHookImpl(traceDispatcher));
        } catch (Throwable e) {
            log.error("system mqtrace hook init failed ,maybe can't send msg trace data");
        }
    }


    @Override
    public void start() {
        try {
            if (started.compareAndSet(false, true)) {
                this.defaultMQProducer.start();
            }
        } catch (Exception e) {
            throw new LMQClientException(e.getMessage());
        }
        if(this.traceDispatcher!=null){
            this.traceDispatcher.registerShutDownHook();
        }
    }


    @Override
    public void shutdown() {
        if (started.compareAndSet(true, false)) {
            this.defaultMQProducer.shutdown();
        }
    }


    @Override
    public SendResult send(final Message message, final String shardingKey) {
        if (UtilAll.isBlank(shardingKey)) {
            throw new LMQClientException("\'shardingKey\' is blank.");
        }

        this.checkMQProducerServiceState(this.defaultMQProducer.getDefaultMQProducerImpl());
        final org.apache.rocketmq.common.message.Message msgRMQ = RMQUtil.msgConvert(message);
        try {
            org.apache.rocketmq.client.producer.SendResult sendResultRMQ =
                    this.defaultMQProducer.send(msgRMQ, new org.apache.rocketmq.client.producer.MessageQueueSelector() {
                        @Override
                        public MessageQueue select(List<MessageQueue> mqs, org.apache.rocketmq.common.message.Message msg,
                                                   Object shardingKey) {
                            int select = Math.abs(shardingKey.hashCode());
                            if (select < 0) {
                                select = 0;
                            }
                            return mqs.get(select % mqs.size());
                        }
                    }, shardingKey);
            message.setMsgID(sendResultRMQ.getMsgId());
            SendResult sendResult = new SendResult();
            sendResult.setTopic(message.getTopic());
            sendResult.setMessageId(sendResultRMQ.getMsgId());
            return sendResult;
        } catch (Exception e) {
            throw new LMQClientException("defaultMQProducer send order exception", e);
        }
    }


    @Override
    public boolean isStarted() {
        return started.get();
    }


    @Override
    public boolean isClosed() {
        return !isStarted();
    }
}

